/*
 * Hash.cpp
 *
 *  Created on: Oct 12, 2012
 *      Author: romina
 */

#include "Hash.h"

Hash::Hash() {

	this->file_log = new ArchivoDeTexto("log_hash.txt", W);
	this->file_bloques = new ManejoDeBloques(TAM_CUBO, PATH_CUBOS);
	this->file_bloques->crear_arch_vacio();
	this->file_tabla = new Tabla(PATH_TABLA);

	if(this->file_tabla->vacia()){
		Cubo cubo;
		unsigned int offset;
		char buffer[TAM_CUBO];
		int id_cubo = this->file_bloques->get_num_prox_bloque();
		cubo.set_td(1);
		cubo.set_id(id_cubo);
		cubo.serializar(buffer, offset = 0);
		if (this->file_bloques->guardar_bloque(buffer) != id_cubo) throw exception();

		/********************************/
		char buffer2[TAM_CUBO];
		this->file_bloques->recuperar_bloque(buffer2, id_cubo);
		cubo.deserializar(buffer2, offset = 0);
		this->file_log->escribirLinea("guarde el bloque vacio => " + cubo.toStringInfo());
		this->file_log->escribirLinea("Inserto primer referencia en la tabla: cubo " + Palabra::intToString(id_cubo));
		/********************************/

		this->file_tabla->referencia_inicial(id_cubo);
	}


}

int Hash::calcular_dispersion(int clave){
	return clave % this->file_tabla->get_tamanio();
}


void Hash::redispersar(Cubo &cubo_i, Cubo &cubo_f){
	list<RecordFrase> registros;
	list<RecordFrase> ::iterator itrecord;

	registros = cubo_i.listar_registros();

	for(itrecord = registros.begin(); itrecord != registros.end(); ++itrecord){

		int id_registro = (*itrecord).getId();
		int pos_cubo_tabla = this->calcular_dispersion(id_registro);
		int id_cubo = this->file_tabla->get_id_cubo(pos_cubo_tabla);

		if(cubo_i.get_id() != id_cubo){
			cubo_i.eliminar_registro((*itrecord).getId());
			cubo_f.insertar_registro((*itrecord));
		}
	}
}

void Hash::inicializar_buffer(char * buffer){

	for (int i = 0; i < TAM_CUBO; i++){
		buffer[i] = char(0);
	}
}

void Hash::overflow(RecordFrase record, Cubo cubo_overflow){
	Cubo cubo_duplicado;
	unsigned int offset;
	int dispersion_inicial = cubo_overflow.get_td();
	char buffer_overflow[TAM_CUBO];
	this->inicializar_buffer(buffer_overflow);
	char buffer_duplicado[TAM_CUBO];
	this->inicializar_buffer(buffer_duplicado);
	int id_cubo_duplicado = this->file_bloques->get_num_prox_bloque();
	this->file_log->escribirLinea("aaaaaaaaaaaaaaaaaaaaaa " + Palabra::intToString(id_cubo_duplicado));
	int posicion = this->calcular_dispersion(record.getId());

	cubo_overflow.duplicar_td();
	cubo_duplicado.set_td(cubo_overflow.get_td());
	cubo_duplicado.set_id(id_cubo_duplicado);

	if(dispersion_inicial == this->file_tabla->get_tamanio()){
		this->file_log->escribirLinea("DUPLICAMOS LA TABLA");
		this->file_tabla->duplicar();
		this->file_tabla->reemplazar(posicion, cubo_duplicado.get_id());
	}
	else{
		this->file_log->escribirLinea("REEMPLAZAMOS REFERENCIAS");
		this->file_tabla->reemplazar_desde(posicion, cubo_duplicado.get_id(), cubo_duplicado.get_td());
	}

	this->redispersar(cubo_overflow, cubo_duplicado);

	cubo_duplicado.serializar(buffer_duplicado, offset = 0);
	this->file_bloques->guardar_bloque(buffer_duplicado, cubo_duplicado.get_id());
	cubo_overflow.serializar(buffer_overflow, offset = 0);
	this->file_bloques->guardar_bloque(buffer_overflow, cubo_overflow.get_id());
	this->file_log->escribirLinea("cubo_overflow final" +  cubo_overflow.toStringInfo());
	this->file_log->escribirLinea("cubo_duplicado final " + cubo_duplicado.toStringInfo() );


}

void Hash::insertar_registro(RecordFrase record){
	Cubo cubo;
	unsigned int offset;
	char buffer_d[TAM_CUBO];
	this->inicializar_buffer(buffer_d);
	char buffer_s[TAM_CUBO];
	this->inicializar_buffer(buffer_s);

	this->file_log->escribirLinea("");
	this->file_log->escribirLinea("");
	this->file_log->escribirLinea("SE INSERTA NUEVO REGISTRO");
	this->file_log->escribirLinea("=========================");

	this->file_log->escribirLinea("TABLA: " + this->file_tabla->toString());
	int pos_cubo = this->calcular_dispersion(record.getId());
	this->file_log->escribirLinea("pos cubo en tabla " + Palabra::intToString(pos_cubo));
	int id_cubo = this->file_tabla->get_id_cubo(pos_cubo);
	this->file_log->escribirLinea("numero cubo " + Palabra::intToString(id_cubo));
	this->file_bloques->recuperar_bloque(buffer_d, id_cubo);

	cubo.deserializar(buffer_d, offset = 0);

	this->file_log->escribirLinea("cubo deserealizado " + cubo.toStringInfo());

	if(!cubo.overflow(record.getTamanio())){
		this->file_log->escribirLinea("NO FUE OVERFLOW");
		cubo.insertar_registro(record);
		cubo.serializar(buffer_s, offset = 0);
		this->file_bloques->guardar_bloque(buffer_s, id_cubo);
		this->file_log->escribirLinea("ESTADO FINAL DEL CUBO => " + cubo.toStringInfo());
	}
	else{
		this->file_log->escribirLinea("FUE OVERFLOW");
		this->overflow(record, cubo);
		this->insertar_registro(record);
	}
}

//si no encuentra el registro lanza una excepcion
RecordFrase Hash::obtener_registro(int clave){
	Cubo cubo;
	RecordFrase record;
	unsigned int offset = 0;
	char buffer[TAM_CUBO];

	int pos_cubo = this->calcular_dispersion(clave);
	int id_cubo = this->file_tabla->get_id_cubo(pos_cubo);

	this->file_bloques->recuperar_bloque(buffer, id_cubo);

	cubo.deserializar(buffer, offset);

	cubo.buscar_registro(clave, record);

	return record;
}

//si no encuentra el registro lanza una excepcion
void Hash::modificar_registro(RecordFrase record){
	Cubo cubo;
	unsigned int offset = 0;
	char buffer[TAM_CUBO];

	int pos_cubo = this->calcular_dispersion(record.getId());
	int id_cubo = this->file_tabla->get_id_cubo(pos_cubo);

	this->file_bloques->recuperar_bloque(buffer, id_cubo);

	cubo.deserializar(buffer, offset);

	cubo.modificar_registro(record);

}

//si no encuentra el registro lanza una excepcion
void Hash::borrar_registro(int clave){
	Cubo cubo;
	unsigned int offset = 0;
	char buffer[TAM_CUBO];

	int pos_cubo = this->calcular_dispersion(clave);
	int id_cubo = this->file_tabla->get_id_cubo(pos_cubo);

	this->file_bloques->recuperar_bloque(buffer, id_cubo);

	cubo.deserializar(buffer, offset);

	cubo.eliminar_registro(clave);
}

string Hash::toString(){
	Cubo cubo;
	char buffer[TAM_CUBO];
	unsigned int offset = 0;
	int cant_cubos = this->file_bloques->get_tam_arch_bloques() / TAM_CUBO;
	//cout << "CANTIDAD DE CUBOS: "<<cant_cubos<<endl;
	string cadena = "";
	for(int i = 0; i < cant_cubos; ++i){
		offset = 0;
		this->file_bloques->recuperar_bloque(buffer, i);
		cubo.deserializar(buffer, offset);
		cadena.append(cubo.toString());
	}

	return cadena;

}

void Hash::show(){
	Cubo cubo;
	int cant_cubos = this->file_bloques->get_tam_arch_bloques() / TAM_CUBO;
	cout << "CANTIDAD DE CUBOS: "<< cant_cubos << endl;
	string cadena = "";
	for(int i = 0; i < cant_cubos; ++i){
		unsigned int offset;
		char buffer[TAM_CUBO];
		this->file_bloques->recuperar_bloque(buffer, i);
		cubo.deserializar(buffer, offset = 0);
		cout << cubo.toStringInfo() << endl;
	}


}

Hash::~Hash() {
	// TODO Auto-generated destructor stub
}




